/*
 ------------------------------------------------------------------------------
	winmad++ -	a test program for using SDL and mad library on Windows
				with object oriented UI style
				with low level mad (seek)

	Copyright (C) 2011 Pierre Veber
	
	This program is free software; you can redistribute it and/or modify
	it under the terms of the GNU General Public License as published by
	the Free Software Foundation; either version 3 of the License, or
	(at your option) any later version.
	
	This program is distributed in the hope that it will be useful,
	but WITHOUT ANY WARRANTY; without even the implied warranty of
	MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
	GNU General Public License for more details.
	
	You should have received a copy of the GNU General Public License
	along with this program; if not, write to the Free Software Foundation,
	Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301  USA
------------------------------------------------------------------------------
*/

#include "precomp.h"

#define THIS_FILTER		TRACE_UI

#define S_LOCK(s)	{ \
						if ( SDL_MUSTLOCK(s) ) \
						{ \
							if ( SDL_LockSurface(s) < 0 ) \
							{ \
								throw "cannot lock surface"; \
							} \
						} \
					}

#define S_UNLOCK(s) { \
						if ( SDL_MUSTLOCK(s) ) \
						{ \
							SDL_UnlockSurface(s); \
						} \
					}

CSDLBase* pUnderMouse = NULL;
audio_data_t audio;


/*
 ===========================================================================
	CSDLBase
 ===========================================================================
*/

CSDLBase::CSDLBase(char* lpszName, CSDLBase* pParent, char* lpszStyle)
{
	this->lpszName = _strdup(lpszName);
	this->pSurface = NULL;
	this->pParent = pParent;
	this->pChildList = NULL;
	this->nChild = 0;
	this->pStyle = new CStyle();

	if (pParent != NULL)
	{
		this->pParent->addChild(this);
	}
	
	if (lpszStyle != NULL)
	{
		this->pStyle->parse(lpszStyle);
	}
}

CSDLBase::~CSDLBase()
{
	if (this->lpszName != NULL)
	{
		free(this->lpszName);
	}
	if (this->pSurface != NULL)
	{
		SDL_FreeSurface(this->pSurface);
	}
}

void CSDLBase::layout(void)
{
	/*	compute position and size of child items in area
		note:	parent x, y, w and h should have been
				updated before by a layout().
	*/
	
	int xc = this->pStyle->padding.left;
	int yc = this->pStyle->padding.top;
	int ih, ymax = 0;

	for (int i=0; i<this->nChild; i++)
	{
		if (this->pChildList[i]->pStyle->display != STYLE_DISPLAY_NONE)
		{
			this->pChildList[i]->x = xc + this->pChildList[i]->pStyle->margin.left + this->pChildList[i]->pStyle->left;
			this->pChildList[i]->y = yc + this->pChildList[i]->pStyle->margin.top + this->pChildList[i]->pStyle->top;

			this->pChildList[i]->w = this->pChildList[i]->pStyle->width;
			this->pChildList[i]->h = this->pChildList[i]->pStyle->height;

			this->pChildList[i]->layout();

			ih = this->pChildList[i]->h + this->pChildList[i]->pStyle->margin.top + this->pChildList[i]->pStyle->top + this->pChildList[i]->pStyle->margin.bottom;
			if (ih > ymax)
			{
				ymax = ih;
			}

			if (this->pChildList[i]->pStyle->display == STYLE_DISPLAY_BLOCK)
			{
				xc = 0;
				yc += ymax;
			}
			else
			{
				xc = this->pChildList[i]->x + this->pChildList[i]->w + this->pChildList[i]->pStyle->margin.right;
			}
		}
	}
}

void CSDLBase::draw(void)
{
	if (this->pSurface != NULL)
	{	
		for (int i=0; i<this->nChild; i++)
		{
			if (this->pChildList[i]->pStyle->display != STYLE_DISPLAY_NONE)
			{
				/* all child must prepare their surface */
				this->pChildList[i]->draw();

				/* now blit them into mine */
				SDL_Rect rcdst = { this->pChildList[i]->x, this->pChildList[i]->y, 0, 0 };
				SDL_BlitSurface(this->pChildList[i]->surface(), NULL, this->pSurface, &rcdst);
			}
		}		
		SDL_UpdateRect(this->pSurface, 0, 0, this->w, this->h);
	}
}

void CSDLBase::addChild(CSDLBase* pChild)
{
	this->pChildList = (CSDLBase**)realloc(this->pChildList, sizeof(CSDLBase*) * ( this->nChild + 1 ));
	this->pChildList[this->nChild] = pChild;
	this->nChild++;
}

void CSDLBase::mouseMove(int x, int y)
{
	// determine if mouse is over a child item
	for (int i=0; i<this->nChild; i++)
	{
		if (    (this->pChildList[i]->x < x) && (x < (this->pChildList[i]->x + this->pChildList[i]->w))
			 && (this->pChildList[i]->y < y) && (y < (this->pChildList[i]->y + this->pChildList[i]->h))    )
		{
			this->pChildList[i]->mouseMove(x, y);
			return;
		}
	}
	TRACE("mouseMove[%S]\n", this->name());
	if (pUnderMouse != this)
	{
		if (pUnderMouse != NULL)
		{
			pUnderMouse->onMouseOut();
		}
		pUnderMouse = this;
	}
	this->onMouseOver();
}

void CSDLBase::mouseClick(int button)
{
	if (pUnderMouse != NULL)
	{
		if (button == SDL_BUTTON_LEFT)
		{
			pUnderMouse->onLeftClick();
		}
		if (button == SDL_BUTTON_RIGHT)
		{
			pUnderMouse->onRightClick();
		}
	}
}

void CSDLBase::onMouseOver()
{

}

void CSDLBase::onMouseOut()
{

}

void CSDLBase::onLeftClick()
{
	TRACE("onLeftClick[%S]\n", this->name());
}

void CSDLBase::onRightClick()
{

}

/*
 ===========================================================================
	CSDLApp
 ===========================================================================
*/

CSDLApp::CSDLApp(char* lpszAppName, char* lpszStyle) : CSDLBase(lpszAppName, NULL, lpszStyle)
{
	if ( waveInit(&audio) != 0)
	{
		TRACE("Unable to initialize audio !\n");
		throw "Unable to initialize audio.";
	}

	if (TTF_Init() == -1)
	{
		TRACE("Could not initialize SDL_TTF: %s\n", TTF_GetError());
		throw "Could not initialize SDL_TTF.";
	}	
	
	if (SDL_Init(SDL_INIT_VIDEO) < 0)
	{
		TRACE("Could not initialize SDL: %s\n", SDL_GetError());
		throw "Could not initialize SDL.";
	}
	
	if (IMG_Init(IMG_INIT_JPG|IMG_INIT_PNG) == -1)
	{
		TRACE("Could not initialize SDL_IMG: %s\n", IMG_GetError());
		throw "Could not initialize SDL_IMG.";
	}	

	this->pSurface = SDL_SetVideoMode(this->pStyle->width, this->pStyle->height, 0, SDL_HWPALETTE);
	if (this->pSurface == NULL)
	{
		TRACE("Couldn't set screen mode: %s\n", SDL_GetError());
		throw "Could not set screen mode.";
	}
	SDL_WM_SetCaption(this->name(), NULL);
}

CSDLApp::~CSDLApp()
{
	TTF_Quit();
	IMG_Quit();
	SDL_Quit();

	waveClose(&audio);
	
	// SDL Quit frees surface
	this->pSurface = NULL;
}

void CSDLApp::draw(void)
{
	if (this->pStyle->lpszBackground != NULL)
	{
		SDL_Surface* pImageSurface = IMG_Load(this->pStyle->lpszBackground);
		if (pImageSurface == NULL)
		{
			TRACE("CSDLBase::draw (%s) - unable to load image [%s].\n", this->name(), this->pStyle->lpszBackground);
			throw "Unable to load image.";
		}
		SDL_BlitSurface(pImageSurface, NULL, this->pSurface, NULL);
	}

	CSDLBase::draw();

	SDL_UpdateRect(this->pSurface, 0, 0, this->w, this->h);
}

int CSDLApp::run(char* lpszCmd)
{
	decode_thread_data_t* pDecode = NULL;
	SDL_Event event;

	//SDL_WM_SetIcon(IMG_Load("res/icon.png"), NULL);

	this->layout();
	this->draw();

	while(SDL_WaitEvent(&event))
	{
		if (event.type == SDL_QUIT)
		{
			return 0;
		}
		else if (event.type == SDL_KEYDOWN)
		{
			switch (event.key.keysym.sym)
			{
				case SDLK_ESCAPE:
					return 0;
			
				case SDLK_RIGHT:
					madSeek(pDecode);
					break;

				case SDLK_SPACE:
					if ( pDecode == NULL )
					{
						pDecode = madDecode(&audio, lpszCmd);
					}
					else
					{
						madPause(pDecode);
					}					
					break;

				default:
					break;
			}
		}
		else if (event.type == SDL_MOUSEMOTION)
		{
			int x, y;
			SDL_GetMouseState(&x, &y);
			this->mouseMove(x, y);
		}
		else if (event.type == SDL_MOUSEBUTTONUP)
		{
			this->mouseClick(event.button.button);
		}
	}
	return 1;
}

/*
 ===========================================================================
	CSDLText
 ===========================================================================
*/

CSDLText::CSDLText(char* lpszName, CSDLBase* pParent, char* lpszStyle) :
CSDLBase(lpszName, pParent, lpszStyle)
{
	this->szu8Text = NULL;
}

CSDLText::~CSDLText()
{

}

void CSDLText::setText(char* szUTF8)
{
	this->szu8Text = szUTF8;
}

void CSDLText::draw()
{
	if (this->szu8Text == NULL)
	{
		TRACE("CSDLText::draw (%s) - No text to draw, skipping.\n", this->name());
		CSDLBase::draw();
		return;
	}

	if (this->pSurface == NULL)
	{
		this->pSurface = SDL_CreateRGBSurface(SDL_SWSURFACE, this->w, this->h, 32, 0, 0, 0, 0);
		if (this->pSurface == NULL)
		{
			TRACE("CSDLText::draw (%s) - unable to create surface [w=%d, h=%d].\n", this->name(), this->w, this->h);
			throw "Unable to create surface.";
		}
	}

	/* create font */
	TTF_Font *font = TTF_OpenFont("FreeSans.ttf", 12);
	if (font == NULL)
	{
		TRACE("CSDLText::draw (%s) - unable to load font [%s].\n", this->name(), "FreeSans.ttf");
		throw "Unable to load font.";
	}

	/* render text */
	SDL_Color cWhite = {0xFF, 0xFF, 0xFF};
	SDL_Surface* pGlyph = TTF_RenderUTF8_Blended(font, this->szu8Text, cWhite);
	
	// Blit with parent contents (copy as background)
	SDL_Rect rcp = { this->x, this->y, this->w, this->h };
	SDL_BlitSurface(this->parentSurface(), &rcp, this->pSurface, NULL);

	// Then blit glyphs
	SDL_BlitSurface(pGlyph, NULL, this->pSurface, NULL);

	// Free surface
	SDL_FreeSurface(pGlyph);

	/* free font */
	TTF_CloseFont(font);
}
